#include "bsp_spi.h"

#define SPI2_SOFT                           0     // 1:使用软件SPI            0:使用硬件SPI 这里使用软件SPI初始化屏,硬件SPI刷屏 
#define SPI2_DMA                            1     //1:使用硬件SPI+DMA      0:仅使用硬件SPI(使用软件SPI时该宏无意义)

// SPI port and pins definition
#define SPI2_PORT                       GPIOB
#define SPI2_SCK_PIN                    GPIO_PIN_3
#define SPI2_MISO_PIN                   GPIO_PIN_4
#define SPI2_MOSI_PIN                   GPIO_PIN_5
#define SPI2_CS_PIN                     GPIO_PIN_6 //NSS

/*!
    \brief      configure different peripheral clocks
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rcu_config(void) {
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_SPI2);
	rcu_periph_clock_enable(RCU_AF);
#if SPI2_DMA
	rcu_periph_clock_enable(RCU_DMA1);
#endif
}

/*!
    \brief      configure the GPIO peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void spi_gpio_config(void) {
	/* 使用SW下载,不使用JTAG下载,管脚用作其它功能 */
	gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);

#if SPI2_SOFT
	gpio_init(SPI2_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SPI2_CS_PIN | SPI2_MOSI_PIN | SPI2_SCK_PIN);
	gpio_init(SPI2_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SPI2_MISO_PIN);
#else
	gpio_pin_remap_config(GPIO_SPI2_REMAP, DISABLE);
	gpio_init(SPI2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SPI2_MOSI_PIN | SPI2_SCK_PIN); // | SPI2_MISO_PIN
	gpio_init(SPI2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SPI2_MISO_PIN);
	gpio_init(SPI2_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SPI2_MOSI_PIN);
	gpio_init(SPI2_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SPI2_CS_PIN);
#endif
}

/**
 * @brief 配置SPI0和SPI2的参数
 *
 * 本函数对SPI0和SPI2进行配置，以满足特定的通信需求。
 * SPI0被配置为全双工模式下的主设备，而SPI2被配置为单向接收模式下的从设备。
 * 这种配置考虑了不同的设备角色和通信场景。
 */
void spi_config(void) {
	// 定义一个spi_parameter_struct类型的结构体变量，用于初始化SPI的参数
	spi_parameter_struct  spi_init_struct;

	/* SPI0 parameter config */
	// 设置SPI0为全双工传输模式
	spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
	// 设置SPI0为 master 模式
	spi_init_struct.device_mode          = SPI_MASTER;
	// 设置数据帧大小为8位
	spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
	// 设置时钟极性和相位
	spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
	// 设置NSS信号为软件控制
	spi_init_struct.nss                  = SPI_NSS_SOFT;
	// 设置预分频系数，决定SPI时钟频率
	spi_init_struct.prescale             = SPI_PSC_256;
	// 设置数据传输为大端模式
	spi_init_struct.endian               = SPI_ENDIAN_MSB;
	// 使用上述配置初始化SPI2
	spi_init(SPI2, &spi_init_struct);
}


#define SPI2_TX_SIZE    8
uint8_t SPI2TX_Buffer[SPI2_TX_SIZE] = {0};

/**
 * @brief  Initializes the DMA for SPI2 TX.
 *
 * This function configures the DMA for SPI2 TX, including setting the transfer direction,
 * memory address, memory increment mode, memory width, transfer count, peripheral address,
 * peripheral increment mode, peripheral width, and priority. It also disables circular mode
 * and memory-to-memory mode, and finally disables the DMA channel.
 */
void DMA_Tx_Init(void) {
	// Initialize the DMA parameter structure for SPI2 TX.
	dma_parameter_struct dma_init_SPI2_TX = {0};

	// Deinitialize DMA1 channel1 to ensure a clean state for SPI2 TX.
	dma_deinit(DMA1, DMA_CH1);

	/* initialize DMA1 channel1(SPI2_TX) */
	// Set the transfer direction to memory to peripheral.
	dma_init_SPI2_TX.direction = DMA_MEMORY_TO_PERIPHERAL;
	// Set the memory address for DMA transfer.
	dma_init_SPI2_TX.memory_addr = (uint32_t)SPI2TX_Buffer;
	// Enable memory increment mode for consecutive data access.
	dma_init_SPI2_TX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
	// Set the memory width to 32 bits.
	dma_init_SPI2_TX.memory_width = DMA_MEMORY_WIDTH_32BIT;
	// Set the number of data to transfer.
	dma_init_SPI2_TX.number = SPI2_TX_SIZE;
	// Set the peripheral address for DMA transfer.
	dma_init_SPI2_TX.periph_addr = (uint32_t)(&SPI_DATA(SPI2));
	// Disable peripheral increment mode as the peripheral address does not change.
	dma_init_SPI2_TX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	// Set the peripheral width to 32 bits.
	dma_init_SPI2_TX.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
	// Set the DMA channel priority to low.
	dma_init_SPI2_TX.priority = DMA_PRIORITY_LOW;
	// Initialize DMA1 channel1 with the configured parameters.
	dma_init(DMA1, DMA_CH1, &dma_init_SPI2_TX);
	// Disable circular mode for DMA transfer.
	dma_circulation_disable(DMA1, DMA_CH1);

	// Disable memory-to-memory mode as we are transferring data to a peripheral.
	dma_memory_to_memory_disable(DMA1, DMA_CH1);
	// Disable DMA1 channel1 to prepare for configuration.
	dma_channel_disable(DMA1, DMA_CH1);
}
/**SPI2 读取大小**/
#define SPI2_RX_SIZE    8
/**SPI2 接收缓存**/
uint8_t SPI2RX_Buffer[SPI2_RX_SIZE] = {0};
/**
 * @brief  初始化DMA接收通道
 *
 * 本函数配置和初始化DMA控制器的接收通道，用于SPI2的接收操作。它设置了数据传输方向、内存地址、
 * 内存递增模式、数据宽度、传输数量、外设地址、外设递增模式、外设数据宽度以及通道优先级。
 * 此外，函数还禁用了循环模式、内存到内存模式，并最终启用DMA通道。
 */
void dma_Rx_Init(void) {
	//初始化结构体
	dma_parameter_struct dma_init_SPI2_RX = {0};

	// 反初始化DMA通道，确保通道处于已知状态
	dma_deinit(DMA1, DMA_CH2);

	// 配置DMA参数结构体
	dma_init_SPI2_RX.direction = DMA_PERIPHERAL_TO_MEMORY;
	dma_init_SPI2_RX.memory_addr = (uint32_t)SPI2RX_Buffer;
	dma_init_SPI2_RX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
	dma_init_SPI2_RX.memory_width = DMA_MEMORY_WIDTH_32BIT;
	dma_init_SPI2_RX.number = SPI2_RX_SIZE;
	dma_init_SPI2_RX.periph_addr = (uint32_t)(&SPI_DATA(SPI2));
	dma_init_SPI2_RX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	dma_init_SPI2_RX.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
	dma_init_SPI2_RX.priority = DMA_PRIORITY_LOW;

	// 根据配置初始化DMA通道
	dma_init(DMA1, DMA_CH2, &dma_init_SPI2_RX);

	// 禁用DMA循环模式
	dma_circulation_disable(DMA1, DMA_CH2);

	// 禁用内存到内存模式
	dma_memory_to_memory_disable(DMA1, DMA_CH2);

	// 禁用DMA通道
	dma_channel_disable(DMA1, DMA_CH2);
}

/**
 * @brief 通过DMA在SPI外设上发送数据
 * 
 * 本函数通过配置DMA控制器来实现SPI外设的数据发送。它首先禁用DMA通道，
 * 然后配置DMA的内存地址和传输数量，最后启用DMA通道开始数据传输。
 * 
 * @param spi_periph SPI外设的基地址
 * @param ndata 指向待发送数据的指针
 * @param Size 待发送数据的大小
 */
void SPIx_Transmit_DMA(uint32_t spi_periph, uint16_t* ndata, uint16_t Size) {
    // 禁用DMA通道，确保配置可以被正确应用
    dma_channel_disable(DMA1, DMA_CH1);

    // 配置DMA通道的内存地址，即数据将要被发送到的地址
    dma_memory_address_config(DMA1, DMA_CH1, (uint32_t)ndata);
    // 配置DMA通道的数据传输数量
    dma_transfer_number_config(DMA1, DMA_CH1, Size);

    // 启用DMA通道，开始数据传输
    dma_channel_enable(DMA1, DMA_CH1);
}




















